{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "41a374b9",
   "metadata": {},
   "source": [
    "#### Notebook style configuration <span style=\"font-weight:200;\">(optional)</span>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ef4f9df",
   "metadata": {},
   "outputs": [],
   "source": [
    "from IPython.core.display import display, HTML\n",
    "style = open(\"./style.css\").read()\n",
    "display(HTML(\"<style>%s</style>\" % style))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43ddcffc",
   "metadata": {},
   "source": [
    "### Table of contents <a name=\"TOC\"></a>\n",
    "\n",
    "* [Introduction](#introduction)\n",
    "* [Single plot](#single_plot)\n",
    "* [Multiple plots](#multiple_plots)\n",
    "* [Ticks and labels](#ticks)\n",
    "* [Exercises](#exercises)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ee58eee6",
   "metadata": {},
   "source": [
    "# Introduction <a name=\"introduction\"></a><span style=\"float:right;\"><a class=\"small\" style=\"color:black; text-decoration: none; \" href=\"#TOC\">[Back to TOC]</a></span>\n",
    "\n",
    "In this introduction, we'll see how to make a [figure](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.figure.html?highlight=figure#matplotlib.pyplot.figure) and play with the different settings such as to improve the rendering. We'll also see how to compose a figure made of several [subplots](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplot.html) with a moderatly complex layout.\n",
    "\n",
    "\n",
    "\n",
    "<img src=\"../images/anatomy.png\" width=\"50%\" align=\"left\" /> <img src=\"../images/subplots.png\" width=\"50%\" />\n",
    "\n",
    "\n",
    "These images come from the [cheatsheets](https://github.com/matplotlib/cheatsheets).\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9886e56e",
   "metadata": {},
   "source": [
    "# Single plot <a name=\"single_plot\"></a> <span style=\"float:right;\"><a class=\"small\" style=\"color:black; text-decoration: none;\" href=\"#TOC\">[Back to TOC]</a></span>\n",
    "\n",
    "We'll start by playing with a very simple exampe (sine and cosine trigonometric functions) using the [plot](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.plot.html) function and see what options are avalaible to us. We'll see there exists a (very) large number of options that allow to obtain quite different outputs. More precisely, you can modify any aspect of a figure and obtain the precise rendering you've in mind. The only difficulty is to know what are these options and how to apply them."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25b32ad5",
   "metadata": {},
   "source": [
    "## Data preparation\n",
    "\n",
    "Before starting any plot, we need first to have some data to plot. Such data can originate from your own experiment or analysis but for the sake of simplicity, we'll generate our own data using the [numpy](https://numpy.org) library. For the sine and cosine functions, we simply generate 257 values linearly spaced between -π and +π and we compute the sine and cosine of these values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "52849b28",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "X = np.linspace(-np.pi, np.pi, 257, endpoint=True)\n",
    "C, S = np.cos(X), np.sin(X)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "99142c93",
   "metadata": {},
   "source": [
    "X is now a numpy array with 257 values ranging from -π to +π (included). C is the cosine (257 values) and S is the sine (257 values). We're ready to plot them.\n",
    "\n",
    "**Note** The standard way of importing maplotlib is to write `import matplotlib.pyplot as plt` and then use the `plt` prefix in front of matplotlib related functions. In some rather rare cases, we also use the `matplotlib` prefix, but most of the time we only need `plt`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c08bf284",
   "metadata": {},
   "source": [
    "## Implicit defaults\n",
    "\n",
    "Let's draw our first figure and observe what the result looks like."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "22b1a627",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.plot(X, C)\n",
    "plt.plot(X, S)\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02d36201",
   "metadata": {},
   "source": [
    "**Note** The value `257` is a bit arbitrary and not that important. However, if you use too few values, the plot will appear \"broken\" (or even wrong in some cases) The reason is that matplotlib uses linear interpolation between points: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "51182f07",
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "plt.plot(X[::32], C[::32])\n",
    "plt.plot(X[::32], S[::32])\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ed62182",
   "metadata": {},
   "source": [
    "Reciprocally, if you use too much values, the plot will be correct but the computing of values and the rendering will be slower."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0105893d",
   "metadata": {},
   "source": [
    "## Explicit defaults\n",
    "\n",
    "Let's now try to do the exact same figure as above, but this time we'll specify everything. To do so, we need to read these defaults from the [maplotlibrc](https://matplotlib.org/stable/tutorials/introductory/customizing.html#customizing-with-matplotlibrc-files) configuration file that is accessible through the `plt.rcParams` variable. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f505bb5",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "p = plt.rcParams\n",
    "\n",
    "fig = plt.figure(figsize = p['figure.figsize'],\n",
    "                 dpi = p['figure.dpi'],\n",
    "                 facecolor = p['figure.facecolor'],\n",
    "                 edgecolor = p['figure.edgecolor'],\n",
    "                 frameon = p['figure.frameon'])\n",
    "\n",
    "ax = plt.subplot()\n",
    "                   \n",
    "ax.plot(X, C, color=\"C0\",\n",
    "              linewidth = p['lines.linewidth'],\n",
    "              linestyle = p['lines.linestyle'])\n",
    "\n",
    "ax.plot(X, S, color=\"C1\",\n",
    "              linewidth = p['lines.linewidth'],\n",
    "              linestyle = p['lines.linestyle'])\n",
    "\n",
    "xmin, xmax = X.min(), X.max()\n",
    "xmargin = p['axes.xmargin']*(xmax - xmin)\n",
    "ax.set_xlim(xmin - xmargin, xmax + xmargin)\n",
    "\n",
    "ymin, ymax = min(C.min(), S.min()), max(C.max(), S.max())\n",
    "ymargin = p['axes.ymargin']*(ymax - ymin)\n",
    "ax.set_ylim(ymin - ymargin, ymax + ymargin)\n",
    "\n",
    "ax.tick_params(axis = \"x\", which=\"major\",\n",
    "               direction = p['xtick.direction'],\n",
    "               length = p['xtick.major.size'],\n",
    "               width = p['xtick.major.width'])\n",
    "\n",
    "ax.tick_params(axis = \"y\", which=\"major\",\n",
    "               direction = p['ytick.direction'],\n",
    "               length = p['ytick.major.size'],\n",
    "               width = p['ytick.major.width'])\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "32083b9a",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"figure.figsize:  \", p['figure.figsize'])\n",
    "print(\"figure.dpi:      \", p['figure.dpi'])\n",
    "print(\"figure.facecolor:\", p['figure.facecolor'])\n",
    "print(\"figure.edgecolor:\", p['figure.edgecolor'])\n",
    "print(\"figure.frameon:  \", p['figure.frameon'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73217d8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "import pandas as pd\n",
    "from IPython.display import display, HTML\n",
    "\n",
    "def defaults(pattern = \"*\"):\n",
    "    p = plt.rcParams\n",
    "    r = re.compile(pattern)\n",
    "    keys = list(filter(r.match, p.keys()))\n",
    "    data = {key:p[key] for key in keys}\n",
    "    frame = pd.DataFrame(data.values(), index=data.keys(),\n",
    "                         columns=['Default value'])\n",
    "    display( frame )\n",
    "    \n",
    "defaults(\"figure*\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3cef3743",
   "metadata": {},
   "source": [
    "## Custom settings\n",
    "\n",
    "As you can see from the script above, pretty much everything can be changed. So let's modify our figure to improve the rendering a bit. Now that we know what are the defaults, we can get back to the implicit version."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e711c98a",
   "metadata": {},
   "source": [
    "### Figure size\n",
    "\n",
    "The plot appears a bit packed so let's first modify the figure size.\n",
    "To do so, we need to create it explicitely in order to specify a size in inches. Let's try 10 inches wide and 3 inches tall. We'll also use an Axes ( `ax` ) which can be considered as a subfigure and we'll increase the DPI (dots per inch) of the figure to get a better resolution in the notebook."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36886c0a",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "# Set default figure size \n",
    "p[\"figure.figsize\"] = (10,3) \n",
    "\n",
    "# Set default figure dpi\n",
    "p[\"figure.dpi\"] = 300\n",
    "\n",
    "fig,ax = plt.subplots()\n",
    "ax.plot(X, C)\n",
    "ax.plot(X, S)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e96a57e",
   "metadata": {},
   "source": [
    "**Important** Since we modified `rcParams`, this will influence all subsequent figures in this notebook, including the top figures."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "92ae7691",
   "metadata": {},
   "source": [
    "### Line styles\n",
    "\n",
    "Solid line is the default line style but there exist several other styles such as dashed lines ( `linestyle=\"--\"` ), dotted lines ( `linestyle=\":\"` ), etc. You can also combine a style with a marker. For example, we can add disc markers at regular intervals. To do that, we specify the `marker` symbol, the marker color and the spacing between markers (else you will have a marker at each data  point)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0cdbeee4",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34337245",
   "metadata": {},
   "source": [
    "### Tick positions\n",
    "\n",
    "Ticks on the x axis are not ideal positioned because they do not show the interesting values (+/-π,+/-π/2) and there are too many ticks on the y axis. Let's change them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a769ab70",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi])\n",
    "ax.set_yticks([-1,0,1])\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4496d3a7",
   "metadata": {},
   "source": [
    "### Tick labels\n",
    "\n",
    "Ticks are now properly placed but their labels are not very explicit. We could guess that 3.142 is π but it would be better to make it explicit. Let's change labels then."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c5a433d",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"0\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1,0,1])\n",
    "ax.set_yticklabels([\"-1\", \"0\", \"+1\"])\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a899a07b",
   "metadata": {},
   "source": [
    "### Spines position\n",
    "\n",
    "Spines are the four lines around our figure and delimiting the data area. Byt default, there are four spines at top/bottom and left/right but we can hide some of them and move the others. Since there are four of them (top/bottom/left/right), we'll hide the top and right and we'll move the bottom and left ones to coordinate 0 (in data space coordinates)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d1ff65e8",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1,1])\n",
    "ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "598474a8",
   "metadata": {},
   "source": [
    "### Z order\n",
    "\n",
    "If you look carefully at the figure above you can see that axis are above the plot. It was already the case previosuly but it was less noticeable. Now with the markers, it is more obvious and pretty annoying. To fix the problem, we need to tell matplotlib to render our sine and cosine plots in front of the axis. To do so, we need to specify a zorder that specify the order of rendering. Elements are rendererd in increasing zorder. Knowing that the axis has a zorder of 0, let's use 10.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2ac4965",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\", zorder=10)\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\", zorder=10)\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1, 1])\n",
    "ax.set_yticklabels([\"-1\",  \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a77509eb",
   "metadata": {},
   "source": [
    "### Legend\n",
    "\n",
    "Let's add a legend in the upper left corner. This only requires adding the keyword argument `label` (that will be used in the legend box) to the plot commands."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2091ce44",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"cosine\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"sine\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1, 1])\n",
    "ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "ax.legend(loc='upper left', frameon=False)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dac14ee8",
   "metadata": {},
   "source": [
    "### Font size\n",
    "\n",
    "The font size of the tick labels is a bit small. Let's increase it a bit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca75e6d8",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"cosine\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"sine\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1, 1])\n",
    "ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "ax.legend(loc='upper left', frameon=False);\n",
    "\n",
    "for label in ax.get_xticklabels() + ax.get_yticklabels():\n",
    "    label.set_fontsize(\"large\");\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77c16242",
   "metadata": {},
   "source": [
    "### Title\n",
    "\n",
    "We're almost done. Let's now add a title on the left of our figure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2fbce4f8",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"cosine\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"sine\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1, 1])\n",
    "ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "ax.legend(loc='upper left', frameon=False);\n",
    "ax.set_title(\"Trigonometric functions\", x=0.1)\n",
    "\n",
    "for label in ax.get_xticklabels() + ax.get_yticklabels():\n",
    "    label.set_fontsize(\"large\")\n",
    "    \n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c95f1c0",
   "metadata": {},
   "source": [
    "## Saving results\n",
    "\n",
    "We can now save our figure in a file using the PDF format. This is a vector format and this means the quality of the figure will be flawless independently of the zoom level."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c554dfb2",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "fig,ax = plt.subplots()\n",
    "\n",
    "ax.plot(X, C, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"cosine\")\n",
    "ax.plot(X, S, marker=\"o\", markevery=(0, 32), markerfacecolor=\"white\",\n",
    "              zorder=10, label=\"sine\")\n",
    "\n",
    "ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "ax.set_yticks([-1, 1])\n",
    "ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "ax.spines['right'].set_visible(False)\n",
    "ax.spines['top'].set_visible(False)\n",
    "ax.spines['bottom'].set_position(('data',0))\n",
    "ax.spines['left'].set_position(('data',0))\n",
    "\n",
    "ax.legend(loc='upper left', frameon=False);\n",
    "\n",
    "for label in ax.get_xticklabels() + ax.get_yticklabels():\n",
    "    label.set_fontsize(\"large\");\n",
    "    \n",
    "plt.savefig(\"../images/01-introduction.pdf\")\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12c14a1e",
   "metadata": {},
   "source": [
    "  \n",
    "---\n",
    "  \n",
    "  \n",
    "#  Multiple plots <a name=\"multiple_plots\"></a> <span style=\"float:right;\"><a class=\"small\" style=\"color:black; text-decoration: none;\" href=\"#TOC\">[Back to TOC]</a></span>\n",
    "\n",
    "So far, we've been dealing with a single plot (or `Axes` in matplotlib terminology) on a figure, but of course, Matplotlib offers the possibility to draw several plots on the same figure. The only difficulty is to express the layout of these different plots. But let's start with someting simple first"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "036f9a87",
   "metadata": {},
   "source": [
    "We want to split our sine and cosine plot in two different plots side by side. To do that we need to create two axes. The most straigthforward way is to use the `subplot` method and to specify the number of rows and columns:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3080e67",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(12,7))\n",
    "nrows, ncols = 2, 3\n",
    "for index in range(1,nrows*ncols+1):\n",
    "    ax = plt.subplot(nrows, ncols, index);\n",
    "    ax.set_title(\"plt.subplot(%d,%d,%d)\" % (nrows, ncols, index), weight=\"bold\")\n",
    "    \n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90592285",
   "metadata": {},
   "source": [
    "The syntax is straigforward as long as you know that Axes indices go from left to right and top to bottom. Back to our example, we need one row and two columns."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b243278e",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(12,3))\n",
    "ax1 = plt.subplot(1,2,1)\n",
    "ax2 = plt.subplot(1,2,2)\n",
    "\n",
    "def plot(ax, X, Y, title=\"\"):\n",
    "    ax.plot(X,Y)\n",
    "    \n",
    "    ax.set_xticks([-np.pi, -np.pi/2, np.pi/2, np.pi])\n",
    "    ax.set_xticklabels([\"-π\", \"-π/2\", \"π/2\", \"π\"])\n",
    "    ax.set_yticks([-1, 1])\n",
    "    ax.set_yticklabels([\"-1\", \"+1\"])\n",
    "\n",
    "    ax.spines['right'].set_visible(False)\n",
    "    ax.spines['top'].set_visible(False)\n",
    "    ax.spines['bottom'].set_position(('data',0))\n",
    "    ax.spines['left'].set_position(('data',0))\n",
    "    \n",
    "    ax.set_title(title, weight=\"bold\")\n",
    "\n",
    "plot(ax1, X, C, \"cosine function\")\n",
    "plot(ax2, X, S, \"sine function\")\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b45750a0",
   "metadata": {},
   "source": [
    "What if we want to have more complex layout with plot of unequal size? In this case, the best is to use the [gridspec](https://matplotlib.org/stable/api/_as_gen/matplotlib.gridspec.GridSpec.html) method. It also involves dividing the figure into rows and columns, but now we can specify the bounds of each plot, i.e. position and extent and their relative size. Let's see some example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44924648",
   "metadata": {},
   "outputs": [],
   "source": [
    "fig = plt.figure(figsize=(10,6))\n",
    "from matplotlib.gridspec import GridSpec\n",
    "    \n",
    "nrows, ncols = 2, 2\n",
    "widths = 1, 2  # Experiment changing 2 to 3,4,5, etc\n",
    "heights = 1, 2 # Experiment changing 2 to 3,4,5, etc\n",
    "G = GridSpec(nrows, ncols, width_ratios = widths, height_ratios=heights)\n",
    "\n",
    "aspect= 'auto' # Experiment with aspect=1\n",
    "ax = plt.subplot(G[0,0], aspect=aspect); ax.plot(X,C,X,S); ax.grid(1)\n",
    "ax = plt.subplot(G[1,0], aspect=aspect); ax.plot(X,C,X,S); ax.grid(1)\n",
    "ax = plt.subplot(G[0,1], aspect=aspect); ax.plot(X,C,X,S); ax.grid(1)\n",
    "ax = plt.subplot(G[1,1], aspect=aspect); ax.plot(X,C,X,S); ax.grid(1)\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87133b2b",
   "metadata": {},
   "source": [
    "Using gridspec, you can specify any layout, the only difficulty being to be able to express what you want to achieve."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "98186ce3",
   "metadata": {},
   "source": [
    "# Ticks and labels <a name=\"ticks\"></a> <span style=\"float:right;\"><a class=\"small\" style=\"color:black; text-decoration: none;\" href=\"#TOC\">[Back to TOC]</a></span>\n",
    "\n",
    "We have already manipulated ticks and tick labels in the previous sections but we only slighlty modified them. In fact, there exists a failry extented machinery in matplotlib that allows you to put ticks at any position using any formats (for the label). Let's start with a very simple example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "df116c8c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.ticker import MultipleLocator\n",
    "\n",
    "fig = plt.figure(figsize=(10,5))\n",
    "ax = plt.subplot()\n",
    "\n",
    "ax.set_xlim(0,10);\n",
    "ax.set_ylim(0,5);\n",
    "\n",
    "ax.xaxis.set_major_locator(MultipleLocator(1.0))\n",
    "ax.yaxis.set_major_locator(MultipleLocator(1.0))\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "215a9a41",
   "metadata": {},
   "source": [
    "This output of this first example is not fundamentally different from what we've seen so far. But the way to obtain this result is different because we use a `MultipleLocator` descriptor that instructs matplotlib to put major ticks at every unit ( `1.0` ) on the x and y axis. But we can also do the same for minor ticks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23b3beef",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.ticker import MultipleLocator\n",
    "\n",
    "fig = plt.figure(figsize=(10,5))\n",
    "ax = plt.subplot()\n",
    "\n",
    "ax.set_xlim(0,10)\n",
    "ax.set_ylim(0,5)\n",
    "\n",
    "ax.xaxis.set_major_locator(MultipleLocator(1.0))\n",
    "ax.yaxis.set_major_locator(MultipleLocator(1.0))\n",
    "\n",
    "ax.xaxis.set_minor_locator(MultipleLocator(0.1))\n",
    "ax.yaxis.set_minor_locator(MultipleLocator(0.1))\n",
    "\n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6182e875",
   "metadata": {},
   "source": [
    "We can also modify the labels under ticls. Let's make thme bold and white on black."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f4e8260",
   "metadata": {},
   "outputs": [],
   "source": [
    "from matplotlib.ticker import MultipleLocator\n",
    "\n",
    "fig = plt.figure(figsize=(10,5))\n",
    "ax = plt.subplot()\n",
    "\n",
    "ax.set_xlim(0,10);\n",
    "ax.set_ylim(0,5);\n",
    "\n",
    "ax.xaxis.set_major_locator(MultipleLocator(1.0));\n",
    "ax.yaxis.set_major_locator(MultipleLocator(1.0));\n",
    "\n",
    "ax.xaxis.set_minor_locator(MultipleLocator(0.1));\n",
    "ax.yaxis.set_minor_locator(MultipleLocator(0.1));\n",
    "\n",
    "for label in ax.yaxis.get_ticklabels() + ax.xaxis.get_ticklabels():\n",
    "    label.set_fontweight(\"bold\");\n",
    "    label.set_fontsize(\"x-small\");\n",
    "    label.set_color(\"white\");\n",
    "    label.set_bbox(dict(facecolor='black', linewidth=1, edgecolor='0.0', pad=2))\n",
    "    \n",
    "plt.show();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fc44e7fb",
   "metadata": {},
   "source": [
    "  \n",
    "---\n",
    "  \n",
    "\n",
    "# Exercises <a name=\"exercises\"></a>  <span style=\"float:right;\"><a class=\"small\" style=\"color:black; text-decoration: none;\" href=\"#TOC\">[Back to TOC]</a></span>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d4858cd",
   "metadata": {},
   "source": [
    "## Better typography \n",
    "\n",
    "We used π/2 to display pi over two to but it would be better to display $\\frac{\\pi}{2}$. How would you do modify the tick labels such a to obtain the same output as in the figure below?\n",
    "\n",
    "**Hint:** You can edit this notebook cell to see how I wrote $\\frac{\\pi}{2}$.\n",
    "\n",
    "<img src=\"../images/01-exercise-1.png\" width=\"100%\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "35e0b850",
   "metadata": {},
   "source": [
    "## Better style \n",
    "\n",
    "Starting from the code template below, you need to write the subplot function to achieve the same result as the figure below. The only difficulty are the arrows at end of x and y axis. To plot them, you can plot them using specific [markers](https://matplotlib.org/stable/api/markers_api.html).\n",
    "\n",
    "<img src=\"../images/01-exercise-2.png\" width=\"100%\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4659400",
   "metadata": {},
   "outputs": [],
   "source": [
    "raise Exception(\"!!! To be completed\")\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def subplot(index, title):    \n",
    "    # To be completed\n",
    "    return ax\n",
    "\n",
    "fig = plt.figure(figsize=(13,5), dpi=300)\n",
    "X = np.linspace (-4,4,200)\n",
    "\n",
    "subplot(1, \"y = cos(x)\").plot(X, np.cos(X), \"C1\")\n",
    "subplot(2, \"y = sin(x)\").plot(X, np.sin(X), \"C1\")\n",
    "subplot(3, \"y = tan(x)\").plot(X, np.tan(X), \"C1\")\n",
    "subplot(4, \"y = cosh(x)\").plot(X, np.cosh(X), \"C1\")\n",
    "subplot(5, \"y = sinh(x)\").plot(X, np.sinh(X), \"C1\")\n",
    "subplot(6, \"y = tanh(x)\").plot(X, np.tanh(X), \"C1\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a3647b60",
   "metadata": {},
   "source": [
    "## Playing with settings\n",
    "\n",
    "Let's wrap everything we've seen so far in order to produce the figure below. To achieve such results, you'll need to set some defaults settings but you'll also need to tweak individual elements once you've created the figure.\n",
    "\n",
    "<img src=\"../images/01-exercise-3.png\" width=\"100%\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50d9d588",
   "metadata": {},
   "source": [
    "----\n",
    "\n",
    "**Copyright (c) 2021 Nicolas P. Rougier**  \n",
    "This work is licensed under a [Creative Commons Attribution 4.0 International License](http://creativecommons.org/licenses/by/4.0/).\n",
    "<br/>\n",
    "Code is licensed under a [2-Clauses BSD license](https://opensource.org/licenses/BSD-2-Clause)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
